home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / xulrunner-1.9.1.5 / components / nsContentPrefService.js < prev    next >
Text File  |  2009-11-08  |  31KB  |  945 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Content Preferences (cpref).
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2006
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Myk Melez <myk@mozilla.org>
  22.  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const Ci = Components.interfaces;
  39. const Cc = Components.classes;
  40. const Cr = Components.results;
  41. const Cu = Components.utils;
  42.  
  43. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  44.  
  45. function ContentPrefService() {
  46.   // If this throws an exception, it causes the getService call to fail,
  47.   // but the next time a consumer tries to retrieve the service, we'll try
  48.   // to initialize the database again, which might work if the failure
  49.   // was due to a temporary condition (like being out of disk space).
  50.   this._dbInit();
  51.  
  52.   // Observe shutdown so we can shut down the database connection.
  53.   this._observerSvc.addObserver(this, "xpcom-shutdown", false);
  54. }
  55.  
  56. ContentPrefService.prototype = {
  57.   //**************************************************************************//
  58.   // XPCOM Plumbing
  59.  
  60.   classDescription: "Content Pref Service",
  61.   classID:          Components.ID("{e6a3f533-4ffa-4615-8eb4-d4e72d883fa7}"),
  62.   contractID:       "@mozilla.org/content-pref/service;1",
  63.   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentPrefService]),
  64.  
  65.  
  66.   //**************************************************************************//
  67.   // Convenience Getters
  68.  
  69.   // Observer Service
  70.   __observerSvc: null,
  71.   get _observerSvc ContentPrefService_get__observerSvc() {
  72.     if (!this.__observerSvc)
  73.       this.__observerSvc = Cc["@mozilla.org/observer-service;1"].
  74.                            getService(Ci.nsIObserverService);
  75.     return this.__observerSvc;
  76.   },
  77.  
  78.   // Console Service
  79.   __consoleSvc: null,
  80.   get _consoleSvc ContentPrefService_get__consoleSvc() {
  81.     if (!this.__consoleSvc)
  82.       this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
  83.                           getService(Ci.nsIConsoleService);
  84.     return this.__consoleSvc;
  85.   },
  86.  
  87.   // Preferences Service
  88.   __prefSvc: null,
  89.   get _prefSvc ContentPrefService_get__prefSvc() {
  90.     if (!this.__prefSvc)
  91.       this.__prefSvc = Cc["@mozilla.org/preferences-service;1"].
  92.                        getService(Ci.nsIPrefBranch2);
  93.     return this.__prefSvc;
  94.   },
  95.  
  96.  
  97.   //**************************************************************************//
  98.   // Destruction
  99.  
  100.   _destroy: function ContentPrefService__destroy() {
  101.     this._observerSvc.removeObserver(this, "xpcom-shutdown");
  102.  
  103.     // Delete references to XPCOM components to make sure we don't leak them
  104.     // (although we haven't observed leakage in tests).  Also delete references
  105.     // in _observers and _genericObservers to avoid cycles with those that
  106.     // refer to us and don't remove themselves from those observer pools.
  107.     for (var i in this) {
  108.       try { this[i] = null }
  109.       // Ignore "setting a property that has only a getter" exceptions.
  110.       catch(ex) {}
  111.     }
  112.   },
  113.  
  114.  
  115.   //**************************************************************************//
  116.   // nsIObserver
  117.  
  118.   observe: function ContentPrefService_observe(subject, topic, data) {
  119.     switch (topic) {
  120.       case "xpcom-shutdown":
  121.         this._destroy();
  122.         break;
  123.     }
  124.   },
  125.  
  126.  
  127.   //**************************************************************************//
  128.   // nsIContentPrefService
  129.  
  130.   getPref: function ContentPrefService_getPref(aURI, aName) {
  131.     if (aURI) {
  132.       var group = this.grouper.group(aURI);
  133.       return this._selectPref(group, aName);
  134.     }
  135.  
  136.     return this._selectGlobalPref(aName);
  137.   },
  138.  
  139.   setPref: function ContentPrefService_setPref(aURI, aName, aValue) {
  140.     // If the pref is already set to the value, there's nothing more to do.
  141.     var currentValue = this.getPref(aURI, aName);
  142.     if (typeof currentValue != "undefined") {
  143.       if (currentValue == aValue)
  144.         return;
  145.     }
  146.     else {
  147.       // If we are in private browsing mode, refuse to set new prefs
  148.       var inPrivateBrowsing = false;
  149.       try { // The Private Browsing service might not be available.
  150.         var pbs = Cc["@mozilla.org/privatebrowsing;1"].
  151.                   getService(Ci.nsIPrivateBrowsingService);
  152.         inPrivateBrowsing = pbs.privateBrowsingEnabled;
  153.       } catch (e) {}
  154.       if (inPrivateBrowsing)
  155.         return;
  156.     }
  157.  
  158.     var settingID = this._selectSettingID(aName) || this._insertSetting(aName);
  159.     var group, groupID, prefID;
  160.     if (aURI) {
  161.       group = this.grouper.group(aURI);
  162.       groupID = this._selectGroupID(group) || this._insertGroup(group);
  163.       prefID = this._selectPrefID(groupID, settingID);
  164.     }
  165.     else {
  166.       group = null;
  167.       groupID = null;
  168.       prefID = this._selectGlobalPrefID(settingID);
  169.     }
  170.  
  171.     // Update the existing record, if any, or create a new one.
  172.     if (prefID)
  173.       this._updatePref(prefID, aValue);
  174.     else
  175.       this._insertPref(groupID, settingID, aValue);
  176.  
  177.     for each (var observer in this._getObservers(aName)) {
  178.       try {
  179.         observer.onContentPrefSet(group, aName, aValue);
  180.       }
  181.       catch(ex) {
  182.         Cu.reportError(ex);
  183.       }
  184.     }
  185.   },
  186.  
  187.   hasPref: function ContentPrefService_hasPref(aURI, aName) {
  188.     // XXX If consumers end up calling this method regularly, then we should
  189.     // optimize this to query the database directly.
  190.     return (typeof this.getPref(aURI, aName) != "undefined");
  191.   },
  192.  
  193.   removePref: function ContentPrefService_removePref(aURI, aName) {
  194.     // If there's no old value, then there's nothing to remove.
  195.     if (!this.hasPref(aURI, aName))
  196.       return;
  197.  
  198.     var settingID = this._selectSettingID(aName);
  199.     var group, groupID, prefID;
  200.     if (aURI) {
  201.       group = this.grouper.group(aURI);
  202.       groupID = this._selectGroupID(group);
  203.       prefID = this._selectPrefID(groupID, settingID);
  204.     }
  205.     else {
  206.       group = null;
  207.       groupID = null;
  208.       prefID = this._selectGlobalPrefID(settingID);
  209.     }
  210.  
  211.     this._deletePref(prefID);
  212.  
  213.     // Get rid of extraneous records that are no longer being used.
  214.     this._deleteSettingIfUnused(settingID);
  215.     if (groupID)
  216.       this._deleteGroupIfUnused(groupID);
  217.  
  218.     for each (var observer in this._getObservers(aName)) {
  219.       try {
  220.         observer.onContentPrefRemoved(group, aName);
  221.       }
  222.       catch(ex) {
  223.         Cu.reportError(ex);
  224.       }
  225.     }
  226.   },
  227.  
  228.   removeGroupedPrefs: function ContentPrefService_removeGroupedPrefs() {
  229.     this._dbConnection.beginTransaction();
  230.     try {
  231.       this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE groupID IS NOT NULL");
  232.       this._dbConnection.executeSimpleSQL("DELETE FROM groups");
  233.       this._dbConnection.commitTransaction();
  234.     }
  235.     catch(ex) {
  236.       this._dbConnection.rollbackTransaction();
  237.       throw ex;
  238.     }
  239.   },
  240.  
  241.   getPrefs: function ContentPrefService_getPrefs(aURI) {
  242.     if (aURI) {
  243.       var group = this.grouper.group(aURI);
  244.       return this._selectPrefs(group);
  245.     }
  246.  
  247.     return this._selectGlobalPrefs();
  248.   },
  249.  
  250.   // A hash of arrays of observers, indexed by setting name.
  251.   _observers: {},
  252.  
  253.   // An array of generic observers, which observe all settings.
  254.   _genericObservers: [],
  255.  
  256.   addObserver: function ContentPrefService_addObserver(aName, aObserver) {
  257.     var observers;
  258.     if (aName) {
  259.       if (!this._observers[aName])
  260.         this._observers[aName] = [];
  261.       observers = this._observers[aName];
  262.     }
  263.     else
  264.       observers = this._genericObservers;
  265.  
  266.     if (observers.indexOf(aObserver) == -1)
  267.       observers.push(aObserver);
  268.   },
  269.  
  270.   removeObserver: function ContentPrefService_removeObserver(aName, aObserver) {
  271.     var observers;
  272.     if (aName) {
  273.       if (!this._observers[aName])
  274.         return;
  275.       observers = this._observers[aName];
  276.     }
  277.     else
  278.       observers = this._genericObservers;
  279.  
  280.     if (observers.indexOf(aObserver) != -1)
  281.       observers.splice(observers.indexOf(aObserver), 1);
  282.   },
  283.  
  284.   /**
  285.    * Construct a list of observers to notify about a change to some setting,
  286.    * putting setting-specific observers before before generic ones, so observers
  287.    * that initialize individual settings (like the page style controller)
  288.    * execute before observers that display multiple settings and depend on them
  289.    * being initialized first (like the content prefs sidebar).
  290.    */
  291.   _getObservers: function ContentPrefService__getObservers(aName) {
  292.     var observers = [];
  293.  
  294.     if (aName && this._observers[aName])
  295.       observers = observers.concat(this._observers[aName]);
  296.     observers = observers.concat(this._genericObservers);
  297.  
  298.     return observers;
  299.   },
  300.  
  301.   _grouper: null,
  302.   get grouper ContentPrefService_get_grouper() {
  303.     if (!this._grouper)
  304.       this._grouper = Cc["@mozilla.org/content-pref/hostname-grouper;1"].
  305.                       getService(Ci.nsIContentURIGrouper);
  306.     return this._grouper;
  307.   },
  308.  
  309.   get DBConnection ContentPrefService_get_DBConnection() {
  310.     return this._dbConnection;
  311.   },
  312.  
  313.  
  314.   //**************************************************************************//
  315.   // Data Retrieval & Modification
  316.  
  317.   __stmtSelectPref: null,
  318.   get _stmtSelectPref ContentPrefService_get__stmtSelectPref() {
  319.     if (!this.__stmtSelectPref)
  320.       this.__stmtSelectPref = this._dbCreateStatement(
  321.         "SELECT prefs.value AS value " +
  322.         "FROM prefs " +
  323.         "JOIN groups ON prefs.groupID = groups.id " +
  324.         "JOIN settings ON prefs.settingID = settings.id " +
  325.         "WHERE groups.name = :group " +
  326.         "AND settings.name = :setting"
  327.       );
  328.  
  329.     return this.__stmtSelectPref;
  330.   },
  331.  
  332.   _selectPref: function ContentPrefService__selectPref(aGroup, aSetting) {
  333.     var value;
  334.  
  335.     try {
  336.       this._stmtSelectPref.params.group = aGroup;
  337.       this._stmtSelectPref.params.setting = aSetting;
  338.  
  339.       if (this._stmtSelectPref.step())
  340.         value = this._stmtSelectPref.row["value"];
  341.     }
  342.     finally {
  343.       this._stmtSelectPref.reset();
  344.     }
  345.  
  346.     return value;
  347.   },
  348.  
  349.   __stmtSelectGlobalPref: null,
  350.   get _stmtSelectGlobalPref ContentPrefService_get__stmtSelectGlobalPref() {
  351.     if (!this.__stmtSelectGlobalPref)
  352.       this.__stmtSelectGlobalPref = this._dbCreateStatement(
  353.         "SELECT prefs.value AS value " +
  354.         "FROM prefs " +
  355.         "JOIN settings ON prefs.settingID = settings.id " +
  356.         "WHERE prefs.groupID IS NULL " +
  357.         "AND settings.name = :name"
  358.       );
  359.  
  360.     return this.__stmtSelectGlobalPref;
  361.   },
  362.  
  363.   _selectGlobalPref: function ContentPrefService__selectGlobalPref(aName) {
  364.     var value;
  365.  
  366.     try {
  367.       this._stmtSelectGlobalPref.params.name = aName;
  368.  
  369.       if (this._stmtSelectGlobalPref.step())
  370.         value = this._stmtSelectGlobalPref.row["value"];
  371.     }
  372.     finally {
  373.       this._stmtSelectGlobalPref.reset();
  374.     }
  375.  
  376.     return value;
  377.   },
  378.  
  379.   __stmtSelectGroupID: null,
  380.   get _stmtSelectGroupID ContentPrefService_get__stmtSelectGroupID() {
  381.     if (!this.__stmtSelectGroupID)
  382.       this.__stmtSelectGroupID = this._dbCreateStatement(
  383.         "SELECT groups.id AS id " +
  384.         "FROM groups " +
  385.         "WHERE groups.name = :name "
  386.       );
  387.  
  388.     return this.__stmtSelectGroupID;
  389.   },
  390.  
  391.   _selectGroupID: function ContentPrefService__selectGroupID(aName) {
  392.     var id;
  393.  
  394.     try {
  395.       this._stmtSelectGroupID.params.name = aName;
  396.  
  397.       if (this._stmtSelectGroupID.step())
  398.         id = this._stmtSelectGroupID.row["id"];
  399.     }
  400.     finally {
  401.       this._stmtSelectGroupID.reset();
  402.     }
  403.  
  404.     return id;
  405.   },
  406.  
  407.   __stmtInsertGroup: null,
  408.   get _stmtInsertGroup ContentPrefService_get__stmtInsertGroup() {
  409.     if (!this.__stmtInsertGroup)
  410.       this.__stmtInsertGroup = this._dbCreateStatement(
  411.         "INSERT INTO groups (name) VALUES (:name)"
  412.       );
  413.  
  414.     return this.__stmtInsertGroup;
  415.   },
  416.  
  417.   _insertGroup: function ContentPrefService__insertGroup(aName) {
  418.     this._stmtInsertGroup.params.name = aName;
  419.     this._stmtInsertGroup.execute();
  420.     return this._dbConnection.lastInsertRowID;
  421.   },
  422.  
  423.   __stmtSelectSettingID: null,
  424.   get _stmtSelectSettingID ContentPrefService_get__stmtSelectSettingID() {
  425.     if (!this.__stmtSelectSettingID)
  426.       this.__stmtSelectSettingID = this._dbCreateStatement(
  427.         "SELECT id FROM settings WHERE name = :name"
  428.       );
  429.  
  430.     return this.__stmtSelectSettingID;
  431.   },
  432.  
  433.   _selectSettingID: function ContentPrefService__selectSettingID(aName) {
  434.     var id;
  435.  
  436.     try {
  437.       this._stmtSelectSettingID.params.name = aName;
  438.  
  439.       if (this._stmtSelectSettingID.step())
  440.         id = this._stmtSelectSettingID.row["id"];
  441.     }
  442.     finally {
  443.       this._stmtSelectSettingID.reset();
  444.     }
  445.  
  446.     return id;
  447.   },
  448.  
  449.   __stmtInsertSetting: null,
  450.   get _stmtInsertSetting ContentPrefService_get__stmtInsertSetting() {
  451.     if (!this.__stmtInsertSetting)
  452.       this.__stmtInsertSetting = this._dbCreateStatement(
  453.         "INSERT INTO settings (name) VALUES (:name)"
  454.       );
  455.  
  456.     return this.__stmtInsertSetting;
  457.   },
  458.  
  459.   _insertSetting: function ContentPrefService__insertSetting(aName) {
  460.     this._stmtInsertSetting.params.name = aName;
  461.     this._stmtInsertSetting.execute();
  462.     return this._dbConnection.lastInsertRowID;
  463.   },
  464.  
  465.   __stmtSelectPrefID: null,
  466.   get _stmtSelectPrefID ContentPrefService_get__stmtSelectPrefID() {
  467.     if (!this.__stmtSelectPrefID)
  468.       this.__stmtSelectPrefID = this._dbCreateStatement(
  469.         "SELECT id FROM prefs WHERE groupID = :groupID AND settingID = :settingID"
  470.       );
  471.  
  472.     return this.__stmtSelectPrefID;
  473.   },
  474.  
  475.   _selectPrefID: function ContentPrefService__selectPrefID(aGroupID, aSettingID) {
  476.     var id;
  477.  
  478.     try {
  479.       this._stmtSelectPrefID.params.groupID = aGroupID;
  480.       this._stmtSelectPrefID.params.settingID = aSettingID;
  481.  
  482.       if (this._stmtSelectPrefID.step())
  483.         id = this._stmtSelectPrefID.row["id"];
  484.     }
  485.     finally {
  486.       this._stmtSelectPrefID.reset();
  487.     }
  488.  
  489.     return id;
  490.   },
  491.  
  492.   __stmtSelectGlobalPrefID: null,
  493.   get _stmtSelectGlobalPrefID ContentPrefService_get__stmtSelectGlobalPrefID() {
  494.     if (!this.__stmtSelectGlobalPrefID)
  495.       this.__stmtSelectGlobalPrefID = this._dbCreateStatement(
  496.         "SELECT id FROM prefs WHERE groupID IS NULL AND settingID = :settingID"
  497.       );
  498.  
  499.     return this.__stmtSelectGlobalPrefID;
  500.   },
  501.  
  502.   _selectGlobalPrefID: function ContentPrefService__selectGlobalPrefID(aSettingID) {
  503.     var id;
  504.  
  505.     try {
  506.       this._stmtSelectGlobalPrefID.params.settingID = aSettingID;
  507.  
  508.       if (this._stmtSelectGlobalPrefID.step())
  509.         id = this._stmtSelectGlobalPrefID.row["id"];
  510.     }
  511.     finally {
  512.       this._stmtSelectGlobalPrefID.reset();
  513.     }
  514.  
  515.     return id;
  516.   },
  517.  
  518.   __stmtInsertPref: null,
  519.   get _stmtInsertPref ContentPrefService_get__stmtInsertPref() {
  520.     if (!this.__stmtInsertPref)
  521.       this.__stmtInsertPref = this._dbCreateStatement(
  522.         "INSERT INTO prefs (groupID, settingID, value) " +
  523.         "VALUES (:groupID, :settingID, :value)"
  524.       );
  525.  
  526.     return this.__stmtInsertPref;
  527.   },
  528.  
  529.   _insertPref: function ContentPrefService__insertPref(aGroupID, aSettingID, aValue) {
  530.     this._stmtInsertPref.params.groupID = aGroupID;
  531.     this._stmtInsertPref.params.settingID = aSettingID;
  532.     this._stmtInsertPref.params.value = aValue;
  533.     this._stmtInsertPref.execute();
  534.     return this._dbConnection.lastInsertRowID;
  535.   },
  536.  
  537.   __stmtUpdatePref: null,
  538.   get _stmtUpdatePref ContentPrefService_get__stmtUpdatePref() {
  539.     if (!this.__stmtUpdatePref)
  540.       this.__stmtUpdatePref = this._dbCreateStatement(
  541.         "UPDATE prefs SET value = :value WHERE id = :id"
  542.       );
  543.  
  544.     return this.__stmtUpdatePref;
  545.   },
  546.  
  547.   _updatePref: function ContentPrefService__updatePref(aPrefID, aValue) {
  548.     this._stmtUpdatePref.params.id = aPrefID;
  549.     this._stmtUpdatePref.params.value = aValue;
  550.     this._stmtUpdatePref.execute();
  551.   },
  552.  
  553.   __stmtDeletePref: null,
  554.   get _stmtDeletePref ContentPrefService_get__stmtDeletePref() {
  555.     if (!this.__stmtDeletePref)
  556.       this.__stmtDeletePref = this._dbCreateStatement(
  557.         "DELETE FROM prefs WHERE id = :id"
  558.       );
  559.  
  560.     return this.__stmtDeletePref;
  561.   },
  562.  
  563.   _deletePref: function ContentPrefService__deletePref(aPrefID) {
  564.     this._stmtDeletePref.params.id = aPrefID;
  565.     this._stmtDeletePref.execute();
  566.   },
  567.  
  568.   __stmtDeleteSettingIfUnused: null,
  569.   get _stmtDeleteSettingIfUnused ContentPrefService_get__stmtDeleteSettingIfUnused() {
  570.     if (!this.__stmtDeleteSettingIfUnused)
  571.       this.__stmtDeleteSettingIfUnused = this._dbCreateStatement(
  572.         "DELETE FROM settings WHERE id = :id " +
  573.         "AND id NOT IN (SELECT DISTINCT settingID FROM prefs)"
  574.       );
  575.  
  576.     return this.__stmtDeleteSettingIfUnused;
  577.   },
  578.  
  579.   _deleteSettingIfUnused: function ContentPrefService__deleteSettingIfUnused(aSettingID) {
  580.     this._stmtDeleteSettingIfUnused.params.id = aSettingID;
  581.     this._stmtDeleteSettingIfUnused.execute();
  582.   },
  583.  
  584.   __stmtDeleteGroupIfUnused: null,
  585.   get _stmtDeleteGroupIfUnused ContentPrefService_get__stmtDeleteGroupIfUnused() {
  586.     if (!this.__stmtDeleteGroupIfUnused)
  587.       this.__stmtDeleteGroupIfUnused = this._dbCreateStatement(
  588.         "DELETE FROM groups WHERE id = :id " +
  589.         "AND id NOT IN (SELECT DISTINCT groupID FROM prefs)"
  590.       );
  591.  
  592.     return this.__stmtDeleteGroupIfUnused;
  593.   },
  594.  
  595.   _deleteGroupIfUnused: function ContentPrefService__deleteGroupIfUnused(aGroupID) {
  596.     this._stmtDeleteGroupIfUnused.params.id = aGroupID;
  597.     this._stmtDeleteGroupIfUnused.execute();
  598.   },
  599.  
  600.   __stmtSelectPrefs: null,
  601.   get _stmtSelectPrefs ContentPrefService_get__stmtSelectPrefs() {
  602.     if (!this.__stmtSelectPrefs)
  603.       this.__stmtSelectPrefs = this._dbCreateStatement(
  604.         "SELECT settings.name AS name, prefs.value AS value " +
  605.         "FROM prefs " +
  606.         "JOIN groups ON prefs.groupID = groups.id " +
  607.         "JOIN settings ON prefs.settingID = settings.id " +
  608.         "WHERE groups.name = :group "
  609.       );
  610.  
  611.     return this.__stmtSelectPrefs;
  612.   },
  613.  
  614.   _selectPrefs: function ContentPrefService__selectPrefs(aGroup) {
  615.     var prefs = Cc["@mozilla.org/hash-property-bag;1"].
  616.                 createInstance(Ci.nsIWritablePropertyBag);
  617.  
  618.     try {
  619.       this._stmtSelectPrefs.params.group = aGroup;
  620.  
  621.       while (this._stmtSelectPrefs.step())
  622.         prefs.setProperty(this._stmtSelectPrefs.row["name"],
  623.                           this._stmtSelectPrefs.row["value"]);
  624.     }
  625.     finally {
  626.       this._stmtSelectPrefs.reset();
  627.     }
  628.  
  629.     return prefs;
  630.   },
  631.  
  632.   __stmtSelectGlobalPrefs: null,
  633.   get _stmtSelectGlobalPrefs ContentPrefService_get__stmtSelectGlobalPrefs() {
  634.     if (!this.__stmtSelectGlobalPrefs)
  635.       this.__stmtSelectGlobalPrefs = this._dbCreateStatement(
  636.         "SELECT settings.name AS name, prefs.value AS value " +
  637.         "FROM prefs " +
  638.         "JOIN settings ON prefs.settingID = settings.id " +
  639.         "WHERE prefs.groupID IS NULL"
  640.       );
  641.  
  642.     return this.__stmtSelectGlobalPrefs;
  643.   },
  644.  
  645.   _selectGlobalPrefs: function ContentPrefService__selectGlobalPrefs() {
  646.     var prefs = Cc["@mozilla.org/hash-property-bag;1"].
  647.                 createInstance(Ci.nsIWritablePropertyBag);
  648.  
  649.     try {
  650.       while (this._stmtSelectGlobalPrefs.step())
  651.         prefs.setProperty(this._stmtSelectGlobalPrefs.row["name"],
  652.                           this._stmtSelectGlobalPrefs.row["value"]);
  653.     }
  654.     finally {
  655.       this._stmtSelectGlobalPrefs.reset();
  656.     }
  657.  
  658.     return prefs;
  659.   },
  660.  
  661.  
  662.   //**************************************************************************//
  663.   // Database Creation & Access
  664.  
  665.   _dbVersion: 3,
  666.  
  667.   _dbSchema: {
  668.     tables: {
  669.       groups:     "id           INTEGER PRIMARY KEY, \
  670.                    name         TEXT NOT NULL",
  671.   
  672.       settings:   "id           INTEGER PRIMARY KEY, \
  673.                    name         TEXT NOT NULL",
  674.   
  675.       prefs:      "id           INTEGER PRIMARY KEY, \
  676.                    groupID      INTEGER REFERENCES groups(id), \
  677.                    settingID    INTEGER NOT NULL REFERENCES settings(id), \
  678.                    value        BLOB"
  679.     },
  680.     indices: {
  681.       groups_idx: {
  682.         table: "groups",
  683.         columns: ["name"]
  684.       },
  685.       settings_idx: {
  686.         table: "settings",
  687.         columns: ["name"]
  688.       },
  689.       prefs_idx: {
  690.         table: "prefs",
  691.         columns: ["groupID", "settingID"]
  692.       }
  693.     }
  694.   },
  695.  
  696.   _dbConnection: null,
  697.  
  698.   _dbCreateStatement: function ContentPrefService__dbCreateStatement(aSQLString) {
  699.     try {
  700.       var statement = this._dbConnection.createStatement(aSQLString);
  701.     }
  702.     catch(ex) {
  703.       Cu.reportError("error creating statement " + aSQLString + ": " +
  704.                      this._dbConnection.lastError + " - " +
  705.                      this._dbConnection.lastErrorString);
  706.       throw ex;
  707.     }
  708.  
  709.     var wrappedStatement = Cc["@mozilla.org/storage/statement-wrapper;1"].
  710.                            createInstance(Ci.mozIStorageStatementWrapper);
  711.     wrappedStatement.initialize(statement);
  712.     return wrappedStatement;
  713.   },
  714.  
  715.   // _dbInit and the methods it calls (_dbCreate, _dbMigrate, and version-
  716.   // specific migration methods) must be careful not to call any method
  717.   // of the service that assumes the database connection has already been
  718.   // initialized, since it won't be initialized until at the end of _dbInit.
  719.  
  720.   _dbInit: function ContentPrefService__dbInit() {
  721.     var dirService = Cc["@mozilla.org/file/directory_service;1"].
  722.                      getService(Ci.nsIProperties);
  723.     var dbFile = dirService.get("ProfD", Ci.nsIFile);
  724.     dbFile.append("content-prefs.sqlite");
  725.  
  726.     var dbService = Cc["@mozilla.org/storage/service;1"].
  727.                     getService(Ci.mozIStorageService);
  728.  
  729.     var dbConnection;
  730.  
  731.     if (!dbFile.exists())
  732.       dbConnection = this._dbCreate(dbService, dbFile);
  733.     else {
  734.       try {
  735.         dbConnection = dbService.openDatabase(dbFile);
  736.       }
  737.       // If the connection isn't ready after we open the database, that means
  738.       // the database has been corrupted, so we back it up and then recreate it.
  739.       catch (e if e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
  740.         dbConnection = this._dbBackUpAndRecreate(dbService, dbFile,
  741.                                                  dbConnection);
  742.       }
  743.  
  744.       // Get the version of the schema in the file.
  745.       var version = dbConnection.schemaVersion;
  746.  
  747.       // Try to migrate the schema in the database to the current schema used by
  748.       // the service.  If migration fails, back up the database and recreate it.
  749.       if (version != this._dbVersion) {
  750.         try {
  751.           this._dbMigrate(dbConnection, version, this._dbVersion);
  752.         }
  753.         catch(ex) {
  754.           Cu.reportError("error migrating DB: " + ex + "; backing up and recreating");
  755.           dbConnection = this._dbBackUpAndRecreate(dbService, dbFile, dbConnection);
  756.         }
  757.       }
  758.     }
  759.  
  760.     // Turn off disk synchronization checking to reduce disk churn and speed up
  761.     // operations when prefs are changed rapidly (such as when a user repeatedly
  762.     // changes the value of the browser zoom setting for a site).
  763.     //
  764.     // Note: this could cause database corruption if the OS crashes or machine
  765.     // loses power before the data gets written to disk, but this is considered
  766.     // a reasonable risk for the not-so-critical data stored in this database.
  767.     //
  768.     // If you really don't want to take this risk, however, just set the
  769.     // toolkit.storage.synchronous pref to 1 (NORMAL synchronization) or 2
  770.     // (FULL synchronization), in which case mozStorageConnection::Initialize
  771.     // will use that value, and we won't override it here.
  772.     if (!this._prefSvc.prefHasUserValue("toolkit.storage.synchronous"))
  773.       dbConnection.executeSimpleSQL("PRAGMA synchronous = OFF");
  774.  
  775.     this._dbConnection = dbConnection;
  776.   },
  777.  
  778.   _dbCreate: function ContentPrefService__dbCreate(aDBService, aDBFile) {
  779.     var dbConnection = aDBService.openDatabase(aDBFile);
  780.  
  781.     try {
  782.       this._dbCreateSchema(dbConnection);
  783.       dbConnection.schemaVersion = this._dbVersion;
  784.     }
  785.     catch(ex) {
  786.       // If we failed to create the database (perhaps because the disk ran out
  787.       // of space), then remove the database file so we don't leave it in some
  788.       // half-created state from which we won't know how to recover.
  789.       dbConnection.close();
  790.       aDBFile.remove(false);
  791.       throw ex;
  792.     }
  793.  
  794.     return dbConnection;
  795.   },
  796.  
  797.   _dbCreateSchema: function ContentPrefService__dbCreateSchema(aDBConnection) {
  798.     this._dbCreateTables(aDBConnection);
  799.     this._dbCreateIndices(aDBConnection);
  800.   },
  801.  
  802.   _dbCreateTables: function ContentPrefService__dbCreateTables(aDBConnection) {
  803.     for (let name in this._dbSchema.tables)
  804.       aDBConnection.createTable(name, this._dbSchema.tables[name]);
  805.   },
  806.  
  807.   _dbCreateIndices: function ContentPrefService__dbCreateIndices(aDBConnection) {
  808.     for (let name in this._dbSchema.indices) {
  809.       let index = this._dbSchema.indices[name];
  810.       let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table +
  811.                       "(" + index.columns.join(", ") + ")";
  812.       aDBConnection.executeSimpleSQL(statement);
  813.     }
  814.   },
  815.  
  816.   _dbBackUpAndRecreate: function ContentPrefService__dbBackUpAndRecreate(aDBService,
  817.                                                                          aDBFile,
  818.                                                                          aDBConnection) {
  819.     aDBService.backupDatabaseFile(aDBFile, "content-prefs.sqlite.corrupt");
  820.  
  821.     // Close the database, ignoring the "already closed" exception, if any.
  822.     // It'll be open if we're here because of a migration failure but closed
  823.     // if we're here because of database corruption.
  824.     try { aDBConnection.close() } catch(ex) {}
  825.  
  826.     aDBFile.remove(false);
  827.  
  828.     let dbConnection = this._dbCreate(aDBService, aDBFile);
  829.  
  830.     return dbConnection;
  831.   },
  832.  
  833.   _dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) {
  834.     if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) {
  835.       aDBConnection.beginTransaction();
  836.       try {
  837.         this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection);
  838.         aDBConnection.schemaVersion = aNewVersion;
  839.         aDBConnection.commitTransaction();
  840.       }
  841.       catch(ex) {
  842.         aDBConnection.rollbackTransaction();
  843.         throw ex;
  844.       }
  845.     }
  846.     else
  847.       throw("no migrator function from version " + aOldVersion +
  848.             " to version " + aNewVersion);
  849.   },
  850.  
  851.   /**
  852.    * If the schema version is 0, that means it was never set, which means
  853.    * the database was somehow created without the schema being applied, perhaps
  854.    * because the system ran out of disk space (although we check for this
  855.    * in _createDB) or because some other code created the database file without
  856.    * applying the schema.  In any case, recover by simply reapplying the schema.
  857.    */
  858.   _dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) {
  859.     this._dbCreateSchema(aDBConnection);
  860.   },
  861.  
  862.   _dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) {
  863.     aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld");
  864.     aDBConnection.createTable("groups", this._dbSchema.tables.groups);
  865.     aDBConnection.executeSimpleSQL(
  866.       "INSERT INTO groups (id, name) " +
  867.       "SELECT id, name FROM groupsOld"
  868.     );
  869.  
  870.     aDBConnection.executeSimpleSQL("DROP TABLE groupers");
  871.     aDBConnection.executeSimpleSQL("DROP TABLE groupsOld");
  872.  
  873.     this._dbCreateIndices(aDBConnection);
  874.   },
  875.  
  876.   _dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) {
  877.     this._dbCreateIndices(aDBConnection);
  878.   }
  879.  
  880. };
  881.  
  882.  
  883. function HostnameGrouper() {}
  884.  
  885. HostnameGrouper.prototype = {
  886.   //**************************************************************************//
  887.   // XPCOM Plumbing
  888.   
  889.   classDescription: "Hostname Grouper",
  890.   classID:          Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"),
  891.   contractID:       "@mozilla.org/content-pref/hostname-grouper;1",
  892.   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentURIGrouper]),
  893.  
  894.  
  895.   //**************************************************************************//
  896.   // nsIContentURIGrouper
  897.  
  898.   group: function HostnameGrouper_group(aURI) {
  899.     var group;
  900.  
  901.     try {
  902.       // Accessing the host property of the URI will throw an exception
  903.       // if the URI is of a type that doesn't have a host property.
  904.       // Otherwise, we manually throw an exception if the host is empty,
  905.       // since the effect is the same (we can't derive a group from it).
  906.  
  907.       group = aURI.host;
  908.       if (!group)
  909.         throw("can't derive group from host; no host in URI");
  910.     }
  911.     catch(ex) {
  912.       // If we don't have a host, then use the entire URI (minus the query,
  913.       // reference, and hash, if possible) as the group.  This means that URIs
  914.       // like about:mozilla and about:blank will be considered separate groups,
  915.       // but at least they'll be grouped somehow.
  916.       
  917.       // This also means that each individual file: URL will be considered
  918.       // its own group.  This seems suboptimal, but so does treating the entire
  919.       // file: URL space as a single group (especially if folks start setting
  920.       // group-specific capabilities prefs).
  921.  
  922.       // XXX Is there something better we can do here?
  923.  
  924.       try {
  925.         var url = aURI.QueryInterface(Ci.nsIURL);
  926.         group = aURI.prePath + url.filePath;
  927.       }
  928.       catch(ex) {
  929.         group = aURI.spec;
  930.       }
  931.     }
  932.  
  933.     return group;
  934.   }
  935. };
  936.  
  937.  
  938. //****************************************************************************//
  939. // XPCOM Plumbing
  940.  
  941. var components = [ContentPrefService, HostnameGrouper];
  942. var NSGetModule = function ContentPrefService_NSGetModule(compMgr, fileSpec) {
  943.   return XPCOMUtils.generateModule(components);
  944. }
  945.